package com.lt.flashcard.bind;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;

import org.zkoss.bind.Property;
import org.zkoss.bind.ValidationContext;
import org.zkoss.bind.sys.SaveFormBinding;
import org.zkoss.bind.validator.AbstractValidator;
import org.zkoss.bind.validator.BeanValidations;
import org.zkoss.lang.Strings;
import org.zkoss.zk.ui.UiException;

/**
 * Provide utility to validate the form base on JSR 303
 * 
 * @author ocsen
 */
public class FormValidator extends AbstractValidator {

	protected Validator getValidator() {
		return BeanValidations.getValidator();
	}

	protected Set<ConstraintViolation<?>> validate(Class clz, String property, Object value) {
		return getValidator().validateValue(clz, property, value, new Class[0]);
	}

	protected void sort(List<ConstraintViolation<?>> viloations) {
		Collections.sort(viloations, new Comparator() {
			public int compare(Object o11, Object o21) {
				ConstraintViolation<?> o1 = (ConstraintViolation<?>) o11;
				ConstraintViolation<?> o2 = (ConstraintViolation<?>) o21;
				String s1 = o1.getConstraintDescriptor().getAnnotation()
						.toString();
				String s2 = o2.getConstraintDescriptor().getAnnotation()
						.toString();
				return s1.compareTo(s2);
			}
		});
	}

	protected void handleConstraintViolation(ValidationContext ctx, String key,
			Set<ConstraintViolation<?>> violations) {
		int s = violations.size();

		if (s == 1) {
			addInvalidMessages(ctx, key,
					new String[] { ((ConstraintViolation) violations.iterator()
							.next()).getMessage() });
		} else if (s > 0) {
			String[] msgs = new String[violations.size()];

			List l = new ArrayList(violations);
			sort(l);

			for (int i = 0; i < msgs.length; i++) {
				msgs[i] = ((ConstraintViolation) l.get(i)).getMessage();
			}
			addInvalidMessages(ctx, key, msgs);
		}
	}

	protected Class getBeanClass(ValidationContext ctx, Object base) {
		return base.getClass();
	}

	public void validate(ValidationContext ctx) {
		Property p = ctx.getProperty();
		Object base = p.getBase();
		String prefix;
		Class clz;
		String pname;
		if ((ctx.getBindContext().getBinding() instanceof SaveFormBinding)) {
			prefix = (String) ctx.getValidatorArg("prefix");
			if (Strings.isEmpty(prefix)) {
				throw new NullPointerException(
						"prefix of message key is empty, did you set prefix argument in @validator?");
			}

			clz = getBeanClass(ctx, base);
			Map<String, Property> beanProps = ctx.getProperties(base);
			pname = null;
			for (Property prop : beanProps.values()) {
				pname = prop.getProperty();
				if (!".".equals(pname)) {
					Set<ConstraintViolation<?>> s = validate(clz, pname,
							prop.getValue());
					if (s.size() > 0)
						handleConstraintViolation(ctx, prefix + pname, s);
				}
			}
		} else {
			throw new UiException("Can be used in form binding only");
		}
	}
}
